/*
 * Created on 2005-9-6
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.siysoft.util;

import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Encoder {

	private static Log log = LogFactory.getLog(Encoder.class);

	private static MessageDigest digest = null;

	private static boolean isInited = false;

	private static Method encodeMethod1_4 = null;

	private static Method decodeMethod1_4 = null;

	private static final String cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
			+ "abcdefghijklmnopqrstuvwxyz" + "0123456789+/";

	private static final int fillchar = '=';

	// URLEncoder.encode(String) has been deprecated in J2SE 1.4.
	// Take advantage of the new method URLEncoder.encode(String, enc)
	// if J2SE 1.4 is used.
	static {
		try {
			Class urlEncoderClass = Class.forName("java.net.URLEncoder");
			encodeMethod1_4 = urlEncoderClass.getMethod("encode", new Class[] {
					String.class, String.class });
		} catch (Exception ex) {
		} // encodeMethod1_4 will be null if exception

		try {
			Class urlDecoderClass = Class.forName("java.net.URLDecoder");
			decodeMethod1_4 = urlDecoderClass.getMethod("decode", new Class[] {
					String.class, String.class });
		} catch (Exception ex) {
		} // decodeMethod1_4 will be null if exception
	}

	private Encoder() {
	}

	/**
	 * This method return a String that has been encrypted as MD5 and then
	 * escaped using Base64.
	 * <p>
	 * This method should be used to encrypt all password for maximum security.
	 * 
	 * @param input
	 *            String the string that need encrypted
	 * @return String the string after encrypted
	 */
	public static synchronized String getMD5_Base64(String input) {
		if (isInited == false) {
			isInited = true;
			try {
				digest = MessageDigest.getInstance("MD5");
			} catch (Exception ex) {
				log
						.fatal(
								"Cannot get MessageDigest. Application may fail to run correctly.",
								ex);
			}
		}
		if (digest == null)
			return input;

		try {
			digest.update(input.getBytes("UTF-8"));

		} catch (java.io.UnsupportedEncodingException ex) {
			log.error("Assertion: This should never occur.");
		}

		byte[] rawData = digest.digest();
		String retValue = encodeBase64(rawData);
		return retValue;
	}

	public static void printByteArray(byte[] bytes) {
		int i = 0;
		for (i = 0; i < bytes.length; i++) {
			System.out.print(bytes[i]);
		}
		System.out.println();
		System.out.println("length=" + i);
	}

	/**
	 * Encodes a String as a base64 String.
	 * 
	 * @param data
	 *            a String to encode.
	 * @return a base64 encoded String.
	 */
	public static String encodeBase64(String data) {
		return encodeBase64(data.getBytes());
	}

	/**
	 * Encodes a byte array into a base64 String.
	 * 
	 * @param data
	 *            a byte array to encode.
	 * @return a base64 encode String.
	 */
	public static String encodeBase64(byte[] data) {
		int c;
		int len = data.length;
		StringBuffer ret = new StringBuffer(((len / 3) + 1) * 4);
		for (int i = 0; i < len; ++i) {
			c = (data[i] >> 2) & 0x3f;
			ret.append(cvt.charAt(c));
			c = (data[i] << 4) & 0x3f;
			if (++i < len)
				c |= (data[i] >> 4) & 0x0f;

			ret.append(cvt.charAt(c));
			if (i < len) {
				c = (data[i] << 2) & 0x3f;
				if (++i < len)
					c |= (data[i] >> 6) & 0x03;

				ret.append(cvt.charAt(c));
			} else {
				++i;
				ret.append((char) fillchar);
			}

			if (i < len) {
				c = data[i] & 0x3f;
				ret.append(cvt.charAt(c));
			} else {
				ret.append((char) fillchar);
			}
		}
		return ret.toString();
	}

	/**
	 * Decodes a base64 String.
	 * 
	 * @param data
	 *            a base64 encoded String to decode.
	 * @param the
	 *            decoded String.
	 */
	public static String decodeBase64(String data) {
		return decodeBase64(data.getBytes());
	}

	/**
	 * Decodes a base64 aray of bytes.
	 * 
	 * @param data
	 *            a base64 encode byte array to decode.
	 * @param the
	 *            decoded String.
	 */
	public static String decodeBase64(byte[] data) {
		int c, c1;
		int len = data.length;
		StringBuffer ret = new StringBuffer((len * 3) / 4);
		for (int i = 0; i < len; ++i) {
			c = cvt.indexOf(data[i]);
			++i;
			c1 = cvt.indexOf(data[i]);
			c = ((c << 2) | ((c1 >> 4) & 0x3));
			ret.append((char) c);
			if (++i < len) {
				c = data[i];
				if (fillchar == c)
					break;

				c = cvt.indexOf((char) c);
				c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
				ret.append((char) c1);
			}

			if (++i < len) {
				c1 = data[i];
				if (fillchar == c1)
					break;

				c1 = cvt.indexOf((char) c1);
				c = ((c << 6) & 0xc0) | c1;
				ret.append((char) c);
			}
		}
		return ret.toString();
	}

	/**
	 * This method just call URLEncoder.encode() so we can get rid of the
	 * deprecation warning from using URLEncoder.encode() directly.
	 * 
	 * @param input
	 *            String
	 * @return String
	 */
	public static String encodeURL(String input) {
		if (encodeMethod1_4 != null) {
			Object[] methodArgsName = new Object[2];
			methodArgsName[0] = input;
			methodArgsName[1] = "UTF-8";

			try {
				return (String) encodeMethod1_4.invoke(null, methodArgsName);
			} catch (Exception ex) {
				throw new RuntimeException(
						"System error invoking URLEncoder.encode() by reflection.");
			}
		} else {
			return URLEncoder.encode(input);
		}
	}

	/**
	 * This method just call URLDecoder.decode() so we can get rid of the
	 * deprecation warning from using URLDecoder.encode() directly.
	 * 
	 * @param input
	 *            String
	 * @return String
	 */
	public static String decodeURL(String input) {
		if (decodeMethod1_4 != null) {
			Object[] methodArgsName = new Object[2];
			methodArgsName[0] = input;
			methodArgsName[1] = "UTF-8";

			try {
				return (String) decodeMethod1_4.invoke(null, methodArgsName);
			} catch (Exception ex) {
				throw new RuntimeException(
						"System error invoking URLDecoder.decode() by reflection.");
			}
		} else {
			return URLDecoder.decode(input);
		}
	}

	/**
	 * 
	 * @param path
	 *            the path, something like this localhost:8080/image/index.html
	 * @return the path after being encoded
	 */
	public static String encodePath(String path) {
		path = removeInvalidUserInURL(path);
		return path;
	}

	/**
	 * This method is used to fix IE spoof url bug: http://originalsite.com % 0
	 * 0 @ www.badsite.com
	 *   <p>
	 *   (remove the space above, I added spaces because McAfee conplain that it
	 *   is a trojan)
	 * @param path
	 *            String
	 * @return String
	 */
	private static String removeInvalidUserInURL(String path) {
		int atIndex = path.lastIndexOf('@');
		if (atIndex != -1) {
			int pecentIndex = path.indexOf('%');
			if ((pecentIndex != -1) && (pecentIndex < atIndex)) {
				return path.substring(atIndex + 1);// get the char right after
													// @
			}
		}
		return path;
	}

	public static final String encodeHex(byte[] bytes) {
		StringBuffer buf = new StringBuffer(bytes.length * 2);
		int i;

		for (i = 0; i < bytes.length; i++) {
			if (((int) bytes[i] & 0xff) < 0x10) {
				buf.append("0");
			}
			buf.append(Long.toString((int) bytes[i] & 0xff, 16));
		}
		return buf.toString();
	}

	public static void main(String[] args) {
		//String str = getMD5_Base64("thisisjiangtangsalt");
		//System.out.println(str);
		System.out.println("is:"+java.net.URLDecoder.decode("http://service.wlotx.com/ebook/%20201005311275287995623/201005311275287995647.jpg")+"!");
	}
}
